package com.xiaoshuidi.cloud.module.finance.service.invoice.impl;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xiaoshuidi.cloud.framework.common.pojo.CommonResult;
import com.xiaoshuidi.cloud.framework.common.pojo.PageResult;
import com.xiaoshuidi.cloud.framework.hangxin.core.dto.InvoicingItemDTO;
import com.xiaoshuidi.cloud.framework.hangxin.core.dto.InvoicingReqDTO;
import com.xiaoshuidi.cloud.framework.redis.sequence.TimebaseSequenceGenerator;
import com.xiaoshuidi.cloud.framework.security.core.LoginUser;
import com.xiaoshuidi.cloud.module.contract.api.ContractRentOrderApi;
import com.xiaoshuidi.cloud.module.contract.enums.bill.OrderPayStatusEnum;
import com.xiaoshuidi.cloud.module.contract.enums.contract.ContractStatusEnum;
import com.xiaoshuidi.cloud.module.contract.vo.ContractInvoiceDetailVO;
import com.xiaoshuidi.cloud.module.contract.vo.OrderFeeDetailVO;
import com.xiaoshuidi.cloud.module.contract.vo.RentOrderExtVO;
import com.xiaoshuidi.cloud.framework.common.exception.ServiceException;
import com.xiaoshuidi.cloud.module.finance.bwclient.pojo.FpBdcInfo;
import com.xiaoshuidi.cloud.module.finance.bwclient.pojo.FpChCreate;
import com.xiaoshuidi.cloud.module.finance.bwclient.pojo.FpCreate;
import com.xiaoshuidi.cloud.module.finance.bwclient.pojo.FpFeeDetail;
import com.xiaoshuidi.cloud.module.finance.controller.admin.invoice.vo.*;
import com.xiaoshuidi.cloud.module.finance.convert.invoice.BaseInvoiceConvert;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.invoice.*;
import com.xiaoshuidi.cloud.module.finance.dal.mysql.invoice.*;
import com.xiaoshuidi.cloud.module.finance.enums.finance.FinanceCustomerTypeEnum;
import com.xiaoshuidi.cloud.module.finance.enums.invoice.BwFpTaxNumEnm;
import com.xiaoshuidi.cloud.module.finance.enums.invoice.BwServiceEnum;
import com.xiaoshuidi.cloud.module.finance.mq.producer.InvoiceQueryDelayProducer;
import com.xiaoshuidi.cloud.module.finance.service.invoice.FinanceInvoiceConfigService;
import com.xiaoshuidi.cloud.module.finance.service.invoice.BaseInvoiceService;
import com.xiaoshuidi.cloud.module.finance.service.invoice.FinanceInvoiceTitleConfigService;
import com.xiaoshuidi.cloud.module.finance.util.invoice.BwServiceUtil;
import com.xiaoshuidi.cloud.module.rooms.api.apartment.ApartmentApi;
import com.xiaoshuidi.cloud.module.rooms.api.apartment.dto.ApartmentListDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;


import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.xiaoshuidi.cloud.module.finance.enums.ErrorCodeConstants.*;

@Slf4j
@Service
public class BaseInvoiceServiceImpl implements BaseInvoiceService {

    @Resource
    private ContractRentOrderApi contractRentOrderApi;
    @Resource
    private FinanceInvoiceConfigService financeInvoiceConfigService;
    @Resource
    private TimebaseSequenceGenerator timebaseSequenceGenerator;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private BaseInvoiceMapper invoiceMapper;
    @Resource
    private FinanceInvoiceRedRecordMapper financeInvoiceRedRecordMapper;
    @Resource
    private InvoiceDetailMapper invoiceDetailMapper;
    @Resource
    private BaiwangInvoiceLogMapper baiwangInvoiceLogMapper;
    @Resource
    private InvoiceLogMapper invoiceLogMapper;
    @Resource
    private com.xiaoshuidi.cloud.framework.hangxin.core.service.InvoiceService invoiceService;
    @Resource
    private InvoiceQueryDelayProducer invoiceQueryDelayProducer;
    @Resource
    private FinanceInvoiceTitleConfigService financeInvoiceTitleConfigService;
    @Resource
    private ApartmentApi apartmentApi;

    /**
     * 蓝票
     */
    private Integer BLUE = 1;
    /**
     * 红票
     */
    private Integer RED = 2;

    @Override
    @Transactional
    public String issueInvoices(LoginUser user, InvoiceIssueReqVO reqVO) {
        try{
            String[] orderIdArray = reqVO.getOrderIds().split(",");
            String[] orderDetailIdArray = reqVO.getFeeDetailIds().split(",");
            Arrays.sort(orderDetailIdArray);
            StringBuffer sbf = new StringBuffer();
            List<Long> orderIdList = new ArrayList<>();
            for (String orderId : orderIdArray) {
                orderIdList.add(Long.valueOf(orderId));
                List<Long> ids = Stream.of(Long.valueOf(orderId)).collect(Collectors.toList());
                CommonResult<List<RentOrderExtVO>> result = contractRentOrderApi.getActualExpensesByOrderIds(ids);
                log.info(" 申请开票订单信息 result == " + JSON.toJSONString(result));
                RentOrderExtVO vo = result.getCheckedData().stream().findFirst().get();
                log.info(" 账单信息表 rentOrderExt == " + JSON.toJSONString(vo));
                if (!OrderPayStatusEnum.PAID.getCode().equals(vo.getPayStatus())) {
                    throw new ServiceException(FINANCE_INVOICE_RENT_ORDER_UNPAID);
                }
                // 已开票 不能重复开票 0 未申请 3 开票失败 4 已红冲 5 已作废
                if (vo.getInvoiceStatus() != 0 && vo.getInvoiceStatus() != 3
                        &&  vo.getInvoiceStatus() != 4 &&  vo.getInvoiceStatus() != 5 ) {
                    throw new ServiceException(FINANCE_INVOICE_RECORD_INVOICED);
                }
                // 支付时间大于1年的不能开票
                if (null != vo.getActualPayTime()) {
                    LocalDateTime nowTime = LocalDateTime.now();
                    LocalDateTime actualPayTime = vo.getActualPayTime().plusYears(1);
                    int timeNum = actualPayTime.compareTo(nowTime);
                    if (timeNum < 0) {
                        throw new ServiceException(FINANCE_INVOICE_RECORD_TIMEOUT);
                    }
                }
                // 开票配置信息设置表
                FinanceInvoiceTitleConfigDO config = financeInvoiceTitleConfigService.get(vo.getApartmentId());
                if (config == null) {
                    throw new ServiceException(FINANCE_INVOICE_CONFIG_APARTMENT_NOT_FOUND);
                }
                // 开票费项及费率
                List<FinanceInvoiceTaxRateDO> taxConfig = financeInvoiceConfigService.getFeeTax(config.getId().intValue());
                Map<String, FinanceInvoiceTaxRateDO> taxMap = taxConfig.stream().collect(Collectors.toMap(FinanceInvoiceTaxRateDO::getFeeName, Function.identity(), (a , b) -> b));
                log.info("开票费项及费率 ::: taxMap === " + JSON.toJSONString(taxMap));
                // 开票信息明细表
                List<OrderFeeDetailVO> feeDetailList = vo.getFeeDetail();
                // 定义年龄分组条件
                Predicate<OrderFeeDetailVO> isRent = p -> p.getFeeName().contains("租金");
                Predicate<OrderFeeDetailVO> isParkingFee = p -> p.getFeeName().contains("停车费");
                // 订单明细分组信息
                Map<String, List<OrderFeeDetailVO>> groupedOrderFeeDetail = feeDetailList.stream()
                        .collect(Collectors.groupingBy(detail -> {
                            if (isRent.test(detail)) return "租金";
                            if (isParkingFee.test(detail)) return "停车费";
                            return detail.getFeeName(); // 不满足上述任何条件的归为其他
                        }));
                if("91310112MA1GCHWL0B".equals(config.getCompanyTaxpayerId())){
                    if(groupedOrderFeeDetail.containsKey("租金")){
                        List<OrderFeeDetailVO> list = groupedOrderFeeDetail.get("租金");
                        List<OrderFeeDetailVO> services = new ArrayList<>();
                        //重做对象
                        list.stream().forEach(it->{
                            OrderFeeDetailVO temp = new OrderFeeDetailVO();
                            BeanUtils.copyProperties(it, temp);
                            services.add(temp);
                            it.setPaidAmount(BigDecimal.valueOf(0.6).multiply(it.getPaidAmount()).setScale(2,BigDecimal.ROUND_HALF_DOWN));
                        });

                        services.stream().forEach(it->{
                            it.setPaidAmount(BigDecimal.valueOf(0.4).multiply(it.getPaidAmount()).setScale(2, BigDecimal.ROUND_HALF_UP));
                            FinanceInvoiceTaxRateDO d0 =  taxMap.get("维修费");
                            it.setFeeId(d0.getFeeId());
                            it.setFeeName(d0.getFeeName());
                        });
                        if(groupedOrderFeeDetail.containsKey("维修费")){
                            groupedOrderFeeDetail.get("维修费").addAll(services);
                        }else{
                            groupedOrderFeeDetail.put("维修费", services);
                        }
                    }
                }
                log.info("订单明细项目开票分组信息：：：" + JSON.toJSONString(groupedOrderFeeDetail));

                // 开票信息分组结果入库操作
                sbf.append("已提交开票信息，编号：");
                if(groupedOrderFeeDetail.containsKey("租金") && taxMap.containsKey("租金")){
                    List<InvoiceDetailDO> items = groupedOrderFeeDetail.get("租金").stream().filter(f -> f.getPaidAmount().compareTo(BigDecimal.ZERO) > 0)
                            .map(f -> getInvoiceDetail(taxMap, f)).collect(Collectors.toList());
                    InvoiceDO invoice = this.invoiceApply(items, vo, config, reqVO, orderId, user);
                    // 开票明细封装
                    List<FpFeeDetail> list = fpFeeDetailConvert(items);
                    // 开票申请信息 fplxdm 发票类型代码 01：增值税专用发票 02：普通发票
                    String fplxdm = "0".equals(invoice.getInvoiceType()) ? "02" : "01";
                    // tdyslxdm  特定约束类型代码 默认00 租金,停车费发票代码 06
                    String tdyslxdm = "06";
                    int isbdc = 1;
                    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); // 创建格式器
                    FpCreate fpCreate = fpCreateConvert(tdyslxdm, fplxdm, isbdc, vo.getStartTime().format(formatter), vo.getEndTime().format(formatter), invoice, list, vo);
                    String fp = BwServiceUtil.fpCreate(fpCreate);
                    log.info("开票返回的报文，开票编号：【" + invoice.getSeqNo() + "】：" + fp);
                    sbf.append(" 【" ).append(invoice.getSeqNo()).append("】");
                    groupedOrderFeeDetail.remove("租金");
                }
                if (groupedOrderFeeDetail.containsKey("停车费") && taxMap.containsKey("停车费")){
                    List<InvoiceDetailDO> items = groupedOrderFeeDetail.get("停车费").stream().filter(f -> f.getPaidAmount().compareTo(BigDecimal.ZERO) > 0)
                            .map(f -> getInvoiceDetail(taxMap, f)).collect(Collectors.toList());
                    InvoiceDO invoice = this.invoiceApply(items, vo, config, reqVO, orderId, user);
                    // 开票明细封装
                    List<FpFeeDetail> list = fpFeeDetailConvert(items);
                    // 开票申请信息 fplxdm 发票类型代码 01：增值税专用发票 02：普通发票
                    String fplxdm = "0".equals(invoice.getInvoiceType()) ? "02" : "01";
                    // tdyslxdm  特定约束类型代码 默认00 租金,停车费发票代码 06
                    String tdyslxdm = "06";
                    int isbdc = 1;
                    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); // 创建格式器
                    FpCreate fpCreate = fpCreateConvert(tdyslxdm, fplxdm, isbdc, vo.getStartTime().format(formatter), vo.getEndTime().format(formatter), invoice, list, vo);
                    String fp = BwServiceUtil.fpCreate(fpCreate);
                    log.info("开票返回的报文，开票编号：【" + invoice.getSeqNo() + "】：" + fp);
                    sbf.append(" 【" ).append(invoice.getSeqNo()).append("】");
                    groupedOrderFeeDetail.remove("停车费");
                }
                if(!groupedOrderFeeDetail.isEmpty()){
                    // 开票明细
                    List<InvoiceDetailDO> items = new ArrayList<>();
                    for (Map.Entry<String, List<OrderFeeDetailVO>> entry : groupedOrderFeeDetail.entrySet()) {
                        //没有税率配置
                        if (!taxMap.containsKey(entry.getKey())) {
                            if(!entry.getKey().equals("水费") && !entry.getKey().contains("中央空调")){
                                continue;
                            }
                        }
                        items.addAll(entry.getValue().stream().filter(f -> f.getPaidAmount().compareTo(BigDecimal.ZERO) > 0)
                                .map(f -> getInvoiceDetail(taxMap, f)).collect(Collectors.toList()));
                    }
                    if(!items.isEmpty()){
                        InvoiceDO invoice = this.invoiceApply(items, vo, config, reqVO, orderId, user);
                        // 开票明细封装
                        List<FpFeeDetail> list = fpFeeDetailConvert(items);
                        // 开票申请信息 fplxdm 发票类型代码 01：增值税专用发票 02：普通发票
                        String fplxdm = "0".equals(invoice.getInvoiceType()) ? "02" : "01";
                        // tdyslxdm  特定约束类型代码 默认00 租金,停车费发票代码 06
                        String tdyslxdm = "00";
                        int isbdc = 0;
                        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); // 创建格式器
                        FpCreate fpCreate = fpCreateConvert(tdyslxdm, fplxdm, isbdc, vo.getStartTime().format(formatter), vo.getEndTime().format(formatter), invoice, list, vo);
                        String fp = BwServiceUtil.fpCreate(fpCreate);
                        log.info("开票返回的报文，开票编号：【" + invoice.getSeqNo() + "】：" + fp);
                        sbf.append(" 【" ).append(invoice.getSeqNo()).append("】");
                    }
                }
            }
            return sbf.toString();
        }catch (Exception e){
            log.error("开票异常 {}", reqVO.getOrderIds(), e);
            throw new ServiceException(1007008015, e.getMessage());
        }

    }

    private InvoiceDO invoiceApply(List<InvoiceDetailDO> items, RentOrderExtVO vo, FinanceInvoiceTitleConfigDO config, InvoiceIssueReqVO reqVO, String orderId, LoginUser user){
        log.info("开票明细 ::: items === " + JSON.toJSONString(items));
        BigDecimal amountWithoutTax = items.stream().map(InvoiceDetailDO::getUnitAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal amount = items.stream().map(InvoiceDetailDO::getTotalAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal taxAmount = items.stream().map(InvoiceDetailDO::getTax).reduce(BigDecimal.ZERO, BigDecimal::add);
        // 开票信息
        InvoiceDO invoice = InvoiceDO.builder()
                .custType(FinanceCustomerTypeEnum.INDIVIDUAL.name())
                .apartmentId(vo.getApartmentId())
                .invoiceType(reqVO.getInvoiceType() == null ? "0" : reqVO.getInvoiceType())
                .orderNo(orderId + "")
                .seqNo("FP" + timebaseSequenceGenerator.YYYYMMDDHHMMSS.next("lease:invoice:seq"))
                // 合计不含税金额
                .amountWithoutTax(amountWithoutTax)
                // 合计税额
                .taxAmount(taxAmount)
                // 合计开票金额
                .amount(amount)
                .status("1")
                .email(reqVO.getEmail())
                .mobile(reqVO.getMobile())
                .createDate(LocalDateTime.now())
                .title(reqVO.getTitle())
                .taxpayerId(config.getCompanyTaxpayerId())
                .build();
        // 新增开票信息记录入库操作
        invoiceMapper.insert(invoice);
        List<Long> orderList = new ArrayList<>();
        orderList.add(Long.valueOf(orderId));
        contractRentOrderApi.orderInvoiceStatus(orderList, invoice.getId(), 1);
        items.forEach(i -> i.setInvoiceId(invoice.getId()));
        invoiceDetailMapper.insertBatch(items);
        InvoiceLogDO invoiceLogDO = createLog(invoice.getId(), BLUE, "申请开票", "电子普票", null);
        invoiceLogDO.setCreator(user.getNickname());
        invoiceLogMapper.insert(invoiceLogDO);
        return invoice;
    }

    @Override
    public String issueInvoices1(LoginUser user, InvoiceIssueReqVO reqVO) {
        String[] orderIdArray = reqVO.getOrderIds().split(",");
        String[] orderDetailIdArray = reqVO.getFeeDetailIds().split(",");
        Arrays.sort(orderDetailIdArray);
        JSONObject jsonObject = new JSONObject();
        List<Long> orderIdList = new ArrayList<>();
        FinanceInvoiceConfigDO config = null;
        Map<String, List<OrderFeeDetailVO>> groupedOrderFeeDetail;
        // 开票信息明细表
        List<OrderFeeDetailVO> feeDetailList = new ArrayList<>();
        Long apartmentId = null;
        for (String orderId : orderIdArray) {
            orderIdList.add(Long.valueOf(orderId));
            List<Long> ids = Stream.of(Long.valueOf(orderId)).collect(Collectors.toList());
            CommonResult<List<RentOrderExtVO>> result = contractRentOrderApi.getActualExpensesByOrderIds(ids);
            log.info(" 申请开票订单信息 result == " + JSON.toJSONString(result));
            RentOrderExtVO vo = result.getCheckedData().stream().findFirst().get();
            Set<Long> idSet = result.getCheckedData().stream().map(it->it.getApartmentId()).collect(Collectors.toSet());
            if(CollectionUtils.isNotEmpty(idSet) && idSet.size() > 1){
                throw new ServiceException(FINANCE_INVOICE_NOT_SAME_APARTMENT);
            }
            log.info(" 账单信息表 rentOrderExt == " + JSON.toJSONString(vo));
            if (!OrderPayStatusEnum.PAID.getCode().equals(vo.getPayStatus())) {
                throw new ServiceException(FINANCE_INVOICE_RENT_ORDER_UNPAID);
            }
            // 已开票 不能重复开票
            if (vo.getInvoiceStatus() != 0) {
                throw new ServiceException(FINANCE_INVOICE_RECORD_INVOICED);
            }
            // 支付时间大于1年的不能开票
            if (null != vo.getActualPayTime()) {
                LocalDateTime nowTime = LocalDateTime.now();
                LocalDateTime actualPayTime = vo.getActualPayTime().plusYears(1);
                int timeNum = actualPayTime.compareTo(nowTime);
                if (timeNum < 0) {
                    throw new ServiceException(FINANCE_INVOICE_RECORD_TIMEOUT);
                }
            }
            // 开票配置信息设置表
            config = financeInvoiceConfigService.getByApartmentId(vo.getApartmentId());
            if (config == null) {
                throw new ServiceException(FINANCE_INVOICE_CONFIG_APARTMENT_NOT_FOUND);
            }
            apartmentId = vo.getApartmentId();
            // 个人开票需要是否需要审核
            if (config.getPersonApplyApproval()) {
                throw new ServiceException(FINANCE_INVOICE_RECORD_PERSONAL_APPROVAL);
            }
            // 开票信息明细表
            feeDetailList.addAll(vo.getFeeDetail());
        }
        // 定义年龄分组条件
        Predicate<OrderFeeDetailVO> isRent = p -> p.getFeeName().contains("租金");
        Predicate<OrderFeeDetailVO> isParkingFee = p -> p.getFeeName().contains("停车费");
        // 订单明细分组信息
        groupedOrderFeeDetail = feeDetailList.stream().collect(Collectors.groupingBy(detail -> {
                    if (isRent.test(detail)) return "租金";
                    if (isParkingFee.test(detail)) return "停车费";
                    return detail.getFeeName();
                }));
        if("91310112MA1GCHWL0B".equals(config.getCompanyTaxpayerId())){
            if(groupedOrderFeeDetail.containsKey("租金")){
                List<OrderFeeDetailVO> list = groupedOrderFeeDetail.get("租金");
                List<OrderFeeDetailVO> services = new ArrayList<>();
                //重做对象
                list.stream().forEach(it->{
                    OrderFeeDetailVO temp = new OrderFeeDetailVO();
                    BeanUtils.copyProperties(it, temp);
                    services.add(temp);
                    it.setPaidAmount(BigDecimal.valueOf(0.6).multiply(it.getPaidAmount()).setScale(2,BigDecimal.ROUND_HALF_DOWN));
                });

                services.stream().forEach(it->{
                    it.setPaidAmount(BigDecimal.valueOf(0.4).multiply(it.getPaidAmount()).setScale(2, BigDecimal.ROUND_HALF_UP));
                    it.setFeeId(18l);
                    it.setFeeName("维修费");
                });
                if(groupedOrderFeeDetail.containsKey("维修费")){
                    groupedOrderFeeDetail.get("维修费").addAll(services);
                }else{
                    groupedOrderFeeDetail.put("维修费", services);
                }
            }
        }

        log.info("订单明细项目开票分组信息：：：" + JSON.toJSONString(groupedOrderFeeDetail));
        // 开票费项及费率
        List<FinanceInvoiceTaxRateDO> taxConfig = financeInvoiceConfigService.getFeeTax(config.getId());
        Map<String, FinanceInvoiceTaxRateDO> taxMap = taxConfig.stream().collect(Collectors.toMap(FinanceInvoiceTaxRateDO::getFeeName, Function.identity(), (k1, k2) -> k2 ));
        log.info("开票费项及费率 ::: taxMap === " + JSON.toJSONString(taxMap));
        // 开票信息分组结果入库操作
        List<InvoiceDetailDO> list = new ArrayList<>();
        for (Map.Entry<String, List<OrderFeeDetailVO>> entry : groupedOrderFeeDetail.entrySet()) {
            // 开票明细
            FinanceInvoiceTaxRateDO taxDo = taxMap.get(entry.getKey());
            List<OrderFeeDetailVO> items = entry.getValue();
            for(OrderFeeDetailVO f : items){
                InvoiceDetailDO temp = getInvoiceDetail(taxMap, f);
                list.add(temp);
            }
        }
        BigDecimal amountWithoutTax = list.stream().map(InvoiceDetailDO::getUnitAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal amount = list.stream().map(InvoiceDetailDO::getTotalAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal taxAmount = list.stream().map(InvoiceDetailDO::getTax).reduce(BigDecimal.ZERO, BigDecimal::add);
        // 开票信息
        InvoiceDO invoice = InvoiceDO.builder()
                .custType(FinanceCustomerTypeEnum.INDIVIDUAL.name())
                .apartmentId(apartmentId)
                .invoiceType(reqVO.getInvoiceType() == null ? "0" : reqVO.getInvoiceType())
                .orderNo("")
                .seqNo("FP" + timebaseSequenceGenerator.YYYYMMDDHHMMSS.next("lease:invoice:seq"))
                // 合计不含税金额
                .amountWithoutTax(amountWithoutTax)
                // 合计税额
                .taxAmount(taxAmount)
                // 合计开票金额
                .amount(amount)
                .status("1")
                .email(reqVO.getEmail())
                .mobile(reqVO.getMobile())
                .createDate(LocalDateTime.now())
                .title(config.getDrawer())
                .taxpayerId(config.getCompanyTaxpayerId())
                .companyAddress(config.getAddress())
                .companyTel(config.getPhone())
                .bank(config.getBankName())
                .bankAccount(config.getBankAccount())
                .remark(reqVO.getBank())
                .build();
        // 新增开票信息记录入库操作
        invoiceMapper.insert(invoice);
        return null;
    }

    @Transactional
    @Override
    public String appIssueInvoices(LoginUser user, AppInvoiceIssueReqVO reqVO) {
        // 获取用户订单信息
        List<Long> orderIdArray = Stream.of(Long.valueOf(reqVO.getOrderId())).collect(Collectors.toList());
        try{
            StringBuffer sbf = new StringBuffer();
            List<Long> orderIdList = new ArrayList<>();
            for (Long orderId : orderIdArray) {
                orderIdList.add(Long.valueOf(orderId));
                List<Long> ids = Stream.of(Long.valueOf(orderId)).collect(Collectors.toList());
                CommonResult<List<RentOrderExtVO>> result = contractRentOrderApi.getActualExpensesByOrderIds(ids);
                log.info(" 申请开票订单信息 result == " + JSON.toJSONString(result));
                RentOrderExtVO vo = result.getCheckedData().stream().findFirst().get();
                log.info(" 账单信息表 rentOrderExt == " + JSON.toJSONString(vo));
                if (!OrderPayStatusEnum.PAID.getCode().equals(vo.getPayStatus())) {
                    throw new ServiceException(FINANCE_INVOICE_RENT_ORDER_UNPAID);
                }
                // 已开票 不能重复开票 0 未申请 3 开票失败 4 已红冲 5 已作废
                if (vo.getInvoiceStatus() != 0 && vo.getInvoiceStatus() != 3
                        &&  vo.getInvoiceStatus() != 4 &&  vo.getInvoiceStatus() != 5 ) {
                    throw new ServiceException(FINANCE_INVOICE_RECORD_INVOICED);
                }
                // 支付时间大于1年的不能开票
                if (null != vo.getActualPayTime()) {
                    LocalDateTime nowTime = LocalDateTime.now();
                    LocalDateTime actualPayTime = vo.getActualPayTime().plusYears(1);
                    int timeNum = actualPayTime.compareTo(nowTime);
                    if (timeNum < 0) {
                        throw new ServiceException(FINANCE_INVOICE_RECORD_TIMEOUT);
                    }
                }
                // 开票配置信息设置表
                FinanceInvoiceTitleConfigDO config = financeInvoiceTitleConfigService.get(vo.getApartmentId());
                if (config == null) {
                    throw new ServiceException(FINANCE_INVOICE_CONFIG_APARTMENT_NOT_FOUND);
                }
                // 开票费项及费率
                List<FinanceInvoiceTaxRateDO> taxConfig = financeInvoiceConfigService.getFeeTax(config.getId().intValue());
                Map<String, FinanceInvoiceTaxRateDO> taxMap = taxConfig.stream().collect(Collectors.toMap(FinanceInvoiceTaxRateDO::getFeeName, Function.identity(), (a , b) -> b));
                log.info("开票费项及费率 ::: taxMap === " + JSON.toJSONString(taxMap));
                // 开票信息明细表
                List<OrderFeeDetailVO> feeDetailList = vo.getFeeDetail();
                // 定义年龄分组条件
                Predicate<OrderFeeDetailVO> isRent = p -> p.getFeeName().contains("租金");
                Predicate<OrderFeeDetailVO> isParkingFee = p -> p.getFeeName().contains("停车费");
                // 订单明细分组信息
                Map<String, List<OrderFeeDetailVO>> groupedOrderFeeDetail = feeDetailList.stream()
                        .collect(Collectors.groupingBy(detail -> {
                            if (isRent.test(detail)) return "租金";
                            if (isParkingFee.test(detail)) return "停车费";
                            return detail.getFeeName(); // 不满足上述任何条件的归为其他
                        }));
                if("91310112MA1GCHWL0B".equals(config.getCompanyTaxpayerId())){
                    if(groupedOrderFeeDetail.containsKey("租金")){
                        List<OrderFeeDetailVO> list = groupedOrderFeeDetail.get("租金");
                        List<OrderFeeDetailVO> services = new ArrayList<>();
                        //重做对象
                        list.stream().forEach(it->{
                            OrderFeeDetailVO temp = new OrderFeeDetailVO();
                            BeanUtils.copyProperties(it, temp);
                            services.add(temp);
                            it.setPaidAmount(BigDecimal.valueOf(0.6).multiply(it.getPaidAmount()).setScale(2,BigDecimal.ROUND_HALF_DOWN));
                        });

                        services.stream().forEach(it->{
                            it.setPaidAmount(BigDecimal.valueOf(0.4).multiply(it.getPaidAmount()).setScale(2, BigDecimal.ROUND_HALF_UP));
                            FinanceInvoiceTaxRateDO d0 =  taxMap.get("维修费");
                            it.setFeeId(d0.getFeeId());
                            it.setFeeName(d0.getFeeName());
                        });
                        if(groupedOrderFeeDetail.containsKey("维修费")){
                            groupedOrderFeeDetail.get("维修费").addAll(services);
                        }else{
                            groupedOrderFeeDetail.put("维修费", services);
                        }
                    }
                }
                log.info("订单明细项目开票分组信息：：：" + JSON.toJSONString(groupedOrderFeeDetail));

                // 开票信息分组结果入库操作
                sbf.append("已提交开票信息，编号：");
                if(groupedOrderFeeDetail.containsKey("租金") && taxMap.containsKey("租金")){
                    List<InvoiceDetailDO> items = groupedOrderFeeDetail.get("租金").stream().filter(f -> f.getPaidAmount().compareTo(BigDecimal.ZERO) > 0)
                            .map(f -> getInvoiceDetail(taxMap, f)).collect(Collectors.toList());
                    InvoiceDO invoice = this.userInvoiceApply(items, vo, config, reqVO, orderId, user);
                    // 开票明细封装
                    List<FpFeeDetail> list = fpFeeDetailConvert(items);
                    // 开票申请信息 fplxdm 发票类型代码 01：增值税专用发票 02：普通发票
                    String fplxdm = "0".equals(invoice.getInvoiceType()) ? "02" : "01";
                    // tdyslxdm  特定约束类型代码 默认00 租金,停车费发票代码 06
                    String tdyslxdm = "06";
                    int isbdc = 1;
                    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); // 创建格式器
                    FpCreate fpCreate = fpCreateConvert(tdyslxdm, fplxdm, isbdc, vo.getStartTime().format(formatter), vo.getEndTime().format(formatter), invoice, list, vo);
                    String fp = BwServiceUtil.fpCreate(fpCreate);
                    log.info("开票返回的报文，开票编号：【" + invoice.getSeqNo() + "】：" + fp);
                    sbf.append(" 【" ).append(invoice.getSeqNo()).append("】");
                    groupedOrderFeeDetail.remove("租金");
                }
                if (groupedOrderFeeDetail.containsKey("停车费") && taxMap.containsKey("停车费")){
                    List<InvoiceDetailDO> items = groupedOrderFeeDetail.get("停车费").stream().filter(f -> f.getPaidAmount().compareTo(BigDecimal.ZERO) > 0)
                            .map(f -> getInvoiceDetail(taxMap, f)).collect(Collectors.toList());
                    InvoiceDO invoice = this.userInvoiceApply(items, vo, config, reqVO, orderId, user);
                    // 开票明细封装
                    List<FpFeeDetail> list = fpFeeDetailConvert(items);
                    // 开票申请信息 fplxdm 发票类型代码 01：增值税专用发票 02：普通发票
                    String fplxdm = "0".equals(invoice.getInvoiceType()) ? "02" : "01";
                    // tdyslxdm  特定约束类型代码 默认00 租金,停车费发票代码 06
                    String tdyslxdm = "06";
                    int isbdc = 1;
                    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); // 创建格式器
                    FpCreate fpCreate = fpCreateConvert(tdyslxdm, fplxdm, isbdc, vo.getStartTime().format(formatter), vo.getEndTime().format(formatter), invoice, list, vo);
                    String fp = BwServiceUtil.fpCreate(fpCreate);
                    log.info("开票返回的报文，开票编号：【" + invoice.getSeqNo() + "】：" + fp);
                    sbf.append(" 【" ).append(invoice.getSeqNo()).append("】");
                    groupedOrderFeeDetail.remove("停车费");
                }
                if(!groupedOrderFeeDetail.isEmpty()){
                    // 开票明细
                    List<InvoiceDetailDO> items = new ArrayList<>();
                    for (Map.Entry<String, List<OrderFeeDetailVO>> entry : groupedOrderFeeDetail.entrySet()) {
                        //没有税率配置
                        if (!taxMap.containsKey(entry.getKey())) {
                            if(!entry.getKey().equals("水费") && !entry.getKey().contains("中央空调")){
                                continue;
                            }
                        }
                        items.addAll(entry.getValue().stream().filter(f -> f.getPaidAmount().compareTo(BigDecimal.ZERO) > 0)
                                .map(f -> getInvoiceDetail(taxMap, f)).collect(Collectors.toList()));
                    }
                    if(!items.isEmpty()){
                        InvoiceDO invoice = this.userInvoiceApply(items, vo, config, reqVO, orderId, user);
                        // 开票明细封装
                        List<FpFeeDetail> list = fpFeeDetailConvert(items);
                        // 开票申请信息 fplxdm 发票类型代码 01：增值税专用发票 02：普通发票
                        String fplxdm = "0".equals(invoice.getInvoiceType()) ? "02" : "01";
                        // tdyslxdm  特定约束类型代码 默认00 租金,停车费发票代码 06
                        String tdyslxdm = "00";
                        int isbdc = 0;
                        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); // 创建格式器
                        FpCreate fpCreate = fpCreateConvert(tdyslxdm, fplxdm, isbdc, vo.getStartTime().format(formatter), vo.getEndTime().format(formatter), invoice, list, vo);
                        String fp = BwServiceUtil.fpCreate(fpCreate);
                        log.info("开票返回的报文，开票编号：【" + invoice.getSeqNo() + "】：" + fp);
                        sbf.append(" 【" ).append(invoice.getSeqNo()).append("】");
                    }
                }
            }
            return sbf.toString();
        }catch (Exception e){
            log.error("开票异常 {}", reqVO.getOrderId(), e);
            throw new ServiceException(1007008015, e.getMessage());
        }
    }

    private InvoiceDO userInvoiceApply(List<InvoiceDetailDO> items, RentOrderExtVO vo, FinanceInvoiceTitleConfigDO config, AppInvoiceIssueReqVO reqVO, Long orderId, LoginUser user){
        log.info("开票明细 ::: items === " + JSON.toJSONString(items));
        BigDecimal amountWithoutTax = items.stream().map(InvoiceDetailDO::getUnitAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal amount = items.stream().map(InvoiceDetailDO::getTotalAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal taxAmount = items.stream().map(InvoiceDetailDO::getTax).reduce(BigDecimal.ZERO, BigDecimal::add);
        // 开票信息
        InvoiceDO invoice = InvoiceDO.builder()
                .custType(FinanceCustomerTypeEnum.INDIVIDUAL.name())
                .apartmentId(vo.getApartmentId())
                .invoiceType(reqVO.getInvoiceType() == null ? "0" : reqVO.getInvoiceType())
                .orderNo(orderId + "")
                .seqNo("FP" + timebaseSequenceGenerator.YYYYMMDDHHMMSS.next("lease:invoice:seq"))
                // 合计不含税金额
                .amountWithoutTax(amountWithoutTax)
                // 合计税额
                .taxAmount(taxAmount)
                // 合计开票金额
                .amount(amount)
                .status("1")
                .email(reqVO.getEmail())
                .mobile(reqVO.getMobile())
                .createDate(LocalDateTime.now())
                .title(reqVO.getTitle())
                .taxpayerId(config.getCompanyTaxpayerId())
                .build();
        // 新增开票信息记录入库操作
        invoiceMapper.insert(invoice);
        List<Long> orderList = new ArrayList<>();
        orderList.add(Long.valueOf(orderId));
        contractRentOrderApi.orderInvoiceStatus(orderList, invoice.getId(), 1);
        items.forEach(i -> i.setInvoiceId(invoice.getId()));
        invoiceDetailMapper.insertBatch(items);
        InvoiceLogDO invoiceLogDO = createLog(invoice.getId(), BLUE, "申请开票", "电子普票", null);
        invoiceLogDO.setCreator(user.getNickname());
        invoiceLogMapper.insert(invoiceLogDO);
        return invoice;
    }
    /***
     * 调用百旺金穗发票接口
     * @param invoice
     * @param items
     */
    private String callApiBaiWang(InvoiceDO invoice, List<InvoiceDetailDO> items, List<Long> orderList) {
        log.info("开票记录信息（invoice） === " + JSON.toJSONString(invoice));
        log.info("开票记录明细（InvoiceDetailDO） === " + JSON.toJSONString(items));
        log.info("开票记录关联的订单ID（orderList） === " + JSON.toJSONString(orderList));
        //费用明细组装
        List<FpFeeDetail> feeDetails = new ArrayList<>();
        int djhh = 1;
        for (InvoiceDetailDO detailDO : items) {
            //不含税金额
            BigDecimal bhsjemx = new BigDecimal(invoice.getAmount().toString());
            //含税金额计算
            BigDecimal hsjemx = BwServiceUtil.hsjeCal(bhsjemx, BwFpTaxNumEnm.getTaxByCode("1100101020200000000"));
            //税额计算
            BigDecimal semx = BwServiceUtil.seCal(bhsjemx, BwFpTaxNumEnm.getTaxByCode("1100101020200000000"));
            // 税率
            BigDecimal tax = new BigDecimal(Double.toString(BwFpTaxNumEnm.getTaxByCode("1100101020200000000")));
            FpFeeDetail fpFeeDetail = FpFeeDetail.builder()
                    //
                    .djhh(djhh + "")
                    .fphxz("0")
                    // 商品名称
                    .spmc("代收电费")
                    // 税收编码
                    .ssbm("1100101020200000000")
                    // 税率
                    .tax(tax)
                    // 含税金额计算
                    .hsje(hsjemx)
                    // 税额计算
                    .se(semx)
                    .build();
            feeDetails.add(fpFeeDetail);
            djhh++;
        }

        //发票基础组装
        BigDecimal bhsje = new BigDecimal(invoice.getAmount().toString());
        //含税金额(价税合计), 小数点2位
        BigDecimal hsje = BwServiceUtil.hsjeCal(bhsje, BwFpTaxNumEnm.getTaxByCode("1100101020200000000"));
        //发票基础组装
        FpCreate fpCreate = FpCreate.builder()
                .djbh(invoice.getSeqNo())
                // 发票类型代码
                .fplxdm("02")
                .kplx("0")
                .zsfs("0")
                .hsbz("0")
                .khmc(invoice.getTitle())
                .khsh(invoice.getTaxpayerId())
                // 银行信息，对公必须穿，个人就不需要了
//                .khdzdh(invoice.getCompanyAddress()+" "+invoice.getCompanyTel())
//                .khyhzh(invoice.getBank() + " "+ invoice.getBankAccount())
                .gmfEmail(invoice.getEmail())
                .hsje(hsje)
                .mxxx(feeDetails)
                .build();
        String fp = BwServiceUtil.fpCreate(fpCreate);
        System.out.println(fp);
        JSONObject jsonObject = JSONObject.parseObject(fp);
        if (StringUtils.equals(jsonObject.get("code").toString(), "-1")) {
            invoice.setStatus("3");
            invoice.setErrorReason(jsonObject.get("renson").toString());
            contractRentOrderApi.orderInvoiceStatus(orderList, invoice.getId(), 3);
        }
        invoiceMapper.updateById(invoice);
        BaiwangInvoiceLogDO.builder()
                .interfaceName(BwServiceEnum.FP_CREATE.getSummary())
                .seqNo(invoice.getSeqNo())
                .dataParams(fpCreate.toString())
                .resultData(fp)
                .build();
        return jsonObject.get("renson").toString();
    }

    /***
     * 开票明细封装，相同科目合并开票
     */

    public List<FpFeeDetail> fpFeeDetailConvert(List<InvoiceDetailDO> items) {
        Map<String, List<InvoiceDetailDO>> map = items.stream().collect(Collectors.groupingBy(InvoiceDetailDO::getTaxCategoryNo));
        List<FpFeeDetail> feeDetails = new ArrayList<>();
        int djhh = 1;
        for(Map.Entry<String, List<InvoiceDetailDO>> entry : map.entrySet()){
            InvoiceDetailDO temp = entry.getValue().get(0);
            String taxCode = entry.getKey();
            //含税金额
            BigDecimal amount = entry.getValue().stream().map(InvoiceDetailDO::getTotalAmount).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            FpFeeDetail fpFeeDetail = FpFeeDetail.builder()
                    .djhh(String.valueOf(djhh))
                    .fphxz("0")
                    .spsl("1")
                    .spmc(temp.getGoodsName())
                    .ssbm(taxCode)
                    .hsje(amount)
                    .tax(temp.getTaxRate())
                    .build();
            //判断是否水费
            if ("1100301010000000000".equals(taxCode)) {
                fpFeeDetail.setYhzcbs("1");
                fpFeeDetail.setZzstsgl("按3%简易征收");
            }
            feeDetails.add(fpFeeDetail);
            djhh += 1;
        }
        return feeDetails;
    }


    /***
     * 开票信息封装
     * @param tdyslxdm  特定约束类型代码 默认00 租金,停车费发票代码 06
     * @param fplxdm 发票类型代码 01：增值税专用发票 02：普通发票
     * @param isbdc 是否是不动产 0否1是 选1时 账期时间不能为空
     * @param startTime 账期开始时间 yyyy-mm-dd
     * @param endTime 账期结束时间 yyyy-mm-dd
     * @param vo 账单明细
     */
    public FpCreate fpCreateConvert(String tdyslxdm, String fplxdm, int isbdc, String startTime, String endTime, InvoiceDO invoice, List<FpFeeDetail> feeDetails, RentOrderExtVO vo) {
        FpCreate fpCreate = new FpCreate();
        fpCreate.setDjbh(invoice.getSeqNo());
        fpCreate.setZsfs("0");
        fpCreate.setKplx("0");
        fpCreate.setHsbz("1");
        fpCreate.setKhmc(invoice.getTitle());
        fpCreate.setTdyslxdm(tdyslxdm);
        fpCreate.setFplxdm(fplxdm);
        //是否增值税
        if ("01".equals(fplxdm)) {
            fpCreate.setKhsh(invoice.getTaxpayerId());
            String khdzdh = invoice.getCompanyAddress() + " " + invoice.getCompanyTel();
            fpCreate.setKhdzdh(khdzdh);
            String khyhzh = invoice.getBank() + " " + invoice.getBankAccount();
            fpCreate.setKhyhzh(khyhzh);
        }

        //是否租金，停车费
        if ("06".equals(tdyslxdm) && isbdc == 1) {
            String zlqqz = startTime + " " + endTime;
            FpBdcInfo fpBdcInfo = FpBdcInfo.builder()
                    .bdcdz("无")
                    .fulladdress("无")
                    .zlqqz(zlqqz)
                    .kdsbz("N")
                    .cqzsh("无")
                    .dw("平方米").build();
            fpCreate.setBdcjyzlfw(fpBdcInfo);
        }

        BigDecimal hsje = feeDetails.stream().map(FpFeeDetail::getHsje).reduce(BigDecimal.ZERO, BigDecimal::add);
        fpCreate.setHsje(hsje);
        fpCreate.setMxxx(feeDetails);
        //对应的房间号+费项名称（租金、水费、电费等）+账期（2024.1.1-2024.1.31）
        Set<String> feeNames = feeDetails.stream().map(FpFeeDetail::getSpmc).collect(Collectors.toSet());
        StringBuffer sbf = new StringBuffer();
        sbf.append(vo.getApartmentName()).append(vo.getRoomName()).append(JSONObject.toJSONString(feeNames)).append(startTime.concat("-").concat(endTime));
        fpCreate.setBz(sbf.toString());
        return fpCreate;
    }

    /***
     * 发票冲红
     */
    public FpChCreate fpChConvert(String djbh, String ydjbh, String chyydm, List<InvoiceDetailDO> items) {
        List<FpFeeDetail> feeDetails = new ArrayList<>();
        for (int i = 0; i < items.size(); i++) {
            InvoiceDetailDO item = items.get(i);
            int djhh = i + 1;

            String taxCode = item.getTaxCategoryNo();

            //不含税金额
            BigDecimal bhsjemx = item.getUnitAmount().setScale(2, BigDecimal.ROUND_HALF_UP);
            //含税金额计算
            BigDecimal hsjemx = BwServiceUtil.hsjeCal(bhsjemx, item.getTaxRate().doubleValue());
            //税额计算
            BigDecimal se = BwServiceUtil.seCal(bhsjemx, item.getTaxRate().doubleValue());
            FpFeeDetail fpFeeDetail = FpFeeDetail.builder()
                    .djhh(String.valueOf(djhh))
                    .spsl(String.valueOf(item.getNum()))
                    .tax(item.getTaxRate())
                    .bhsdj(bhsjemx)
                    .bhsje(bhsjemx)
                    .hsje(hsjemx)
                    .se(se)
                    .build();
            feeDetails.add(fpFeeDetail);
        }

        BigDecimal hsje = feeDetails.stream().map(FpFeeDetail::getHsje).reduce(BigDecimal.ZERO, BigDecimal::add);
        FpChCreate fpChCreate = FpChCreate.builder()
                .djbh(djbh)
                .ydjbh(ydjbh)
                .hsje(hsje)
                .chyydm(chyydm)
                .mxxx(feeDetails)
                .build();
        return fpChCreate;
    }


    private void callApi(InvoicingReqDTO reqDTO) {
        invoiceService.apply(reqDTO);
        invoiceQueryDelayProducer.send(reqDTO.getFPQQLSH());
    }

    private void callApi(InvoiceDO invoice, List<InvoiceDetailDO> items,
                         FinanceInvoiceConfigDO config) {
        callApi(convert(invoice, items, config, "0"));
    }

    private DecimalFormat decimalFormat = new DecimalFormat("#0.000000");

    private InvoicingReqDTO convert(InvoiceDO record, List<InvoiceDetailDO> items, FinanceInvoiceConfigDO config, String KPLX) {
        InvoicingReqDTO reqDTO = new InvoicingReqDTO();
        reqDTO.setAuthorizationCode(config.getHangxinAuthCode());
        reqDTO.setTaxpayerId(config.getCompanyTaxpayerId());
        reqDTO.setFPQQLSH(record.getSeqNo());
        reqDTO.setDDH(record.getOrderNo());
        // 0蓝票1红票
        reqDTO.setKPLX(KPLX);
        reqDTO.setBMB_BBH("33.0");
        reqDTO.setXSF_NSRSBH(config.getCompanyTaxpayerId());
        reqDTO.setXSF_MC(config.getCompanyName());
        reqDTO.setXSF_DZDH(config.getAddress() + config.getPhone());
        reqDTO.setXSF_YHZH(config.getBankAccount());
        reqDTO.setGMF_MC(record.getTitle());
        reqDTO.setKPR(config.getDrawer());
        reqDTO.setSKR(config.getPayee());
        reqDTO.setFHR(config.getReviewer());
        reqDTO.setJSHJ(decimalFormat.format(record.getAmount()));
        reqDTO.setHJJE(decimalFormat.format(record.getAmountWithoutTax()));
        reqDTO.setHJSE(decimalFormat.format(record.getTaxAmount()));
        // 81电子专票 其它电子普票
        reqDTO.setFPZL("82");
        reqDTO.setItems(items.stream().map(i -> {
            InvoicingItemDTO item = new InvoicingItemDTO();
            // 发票性质行
            item.setFPHXZ("0");
            item.setXMMC(i.getName());
            item.setGGXH(i.getSpecName());
            item.setDW(i.getUnit());
            item.setXMSL(i.getNum().toString());
            item.setXMDJ(decimalFormat.format((i.getUnitAmount())));
            item.setXMJE(decimalFormat.format(i.getTotalAmount()));
            item.setSL(decimalFormat.format(i.getTaxRate()));
            item.setSE(decimalFormat.format(i.getTax()));
            item.setSPBM(i.getTaxCategoryNo());
            item.setYHZCBS("0");
            return item;
        }).collect(Collectors.toList()));
        return reqDTO;
    }

    /**
     * 开票操作日志
     *
     * @param invoiceId 开票记录
     * @param type      1蓝票2红票
     * @param stage     步骤
     * @param content   内容
     * @param remark    备注
     * @return
     */
    private InvoiceLogDO createLog(Long invoiceId, Integer type,
                                   String stage, String content, String remark) {
        return InvoiceLogDO.builder()
                .invoiceId(invoiceId)
                .type(type)
                .stage(stage)
                .content(content)
                .remark(remark)
                .build();
    }

    private InvoiceDetailDO getInvoiceDetail(Map<String, FinanceInvoiceTaxRateDO> taxMap, OrderFeeDetailVO fee) {
        FinanceInvoiceTaxRateDO taxDo = taxMap.get(fee.getFeeName());
        if(taxDo == null){
            if("水费".equals(fee.getFeeName())){
                taxDo = taxMap.get("冷水费");
            }
            if(fee.getFeeName().contains("中央空调")){
                taxDo = taxMap.get("电费");
            }
            if(taxDo == null){
                throw new ServiceException(FINANCE_INVOICE_RENT_ORDER_FEE_NO_CONFIG);
            }
        }
        // 不含税金额 = 含税金额/(1+税率) 四舍五入
        BigDecimal unitAmount = fee.getPaidAmount().divide(BigDecimal.ONE.add(taxDo.getTaxRate()), 6, RoundingMode.HALF_UP);
        // 税额 = 含税金额 - 不含税金额
        BigDecimal tax1 = fee.getPaidAmount().subtract(unitAmount.setScale(2, RoundingMode.HALF_UP));
        return InvoiceDetailDO.builder()
                .orderfeesId(fee.getId())
                .type(BLUE)
                .name(taxDo.getName())
                .feeId(fee.getFeeId())
                .feeName(taxDo.getName())
                .goodsName(taxDo.getGoodsName())
                .num(1)
                .unitAmount(unitAmount)
                .totalAmount(fee.getPaidAmount())
                .taxRate(taxDo.getTaxRate())
                .tax(tax1)
                .taxCategoryNo(taxDo.getTaxCategoryNo())
                .createTime(LocalDateTime.now())
                .build();
    }


    /**
     * 不含税金额 = 含税金额/(1+税率)
     * 税额= 含税金额 - 不含税金额
     *
     * @return
     */
    private BigDecimal getAmountWithoutTax(BigDecimal amount, BigDecimal taxRate) {
        return amount.divide(BigDecimal.ONE.add(taxRate), 6, RoundingMode.HALF_UP);
    }

    @Override
    public InvoiceInfoRespVO getInvoiceDetailByOrder(Long invoiceId) {
        InvoiceDO invoice = invoiceMapper.selectById(invoiceId);
        if (null == invoice) {
            throw new ServiceException(FINANCE_INVOICE_RECORD_NOT_CONFIG);
        }
        CommonResult<List<ContractInvoiceDetailVO>> result = contractRentOrderApi.getOrderDetailList(invoiceId);
        List<ContractInvoiceDetailVO> orderDetailList = result.getData();
        LambdaQueryWrapper<InvoiceDetailDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(InvoiceDetailDO::getInvoiceId, invoice.getId());
        List<InvoiceDetailDO> invoiceDetailDOList = invoiceDetailMapper.selectList(wrapper);
        LambdaQueryWrapper<InvoiceLogDO> logWrapper = new LambdaQueryWrapper<>();
        logWrapper.eq(InvoiceLogDO::getInvoiceId, invoice.getId());
        List<InvoiceLogDO> invoiceLogDOList = invoiceLogMapper.selectList(logWrapper);
        InvoiceInfoRespVO respVo = new InvoiceInfoRespVO();
        BaseInvoiceVO invoiceVO = BaseInvoiceConvert.INSTANCE.convert(invoice);
        invoiceVO.setInvoiceAmount("0");
        respVo.setInvoice(invoiceVO);
        respVo.setOrderDetailList(orderDetailList);
        respVo.setInvoiceDetailList(invoiceDetailDOList);
        respVo.setInvoiceLogList(invoiceLogDOList);
        return respVo;
    }

    @Override
    public InvoiceInfoRespVO getInvoiceDetailByInvoice(Long invoiceId) {
        InvoiceDO invoice = invoiceMapper.selectById(invoiceId);
        if (null == invoice) {
            throw new ServiceException(FINANCE_INVOICE_RECORD_NOT_CONFIG);
        }
        CommonResult<List<ContractInvoiceDetailVO>> result = contractRentOrderApi.getOrderDetailListByOrderNo(invoiceId);
        List<ContractInvoiceDetailVO> orderDetailList = result.getData();
        LambdaQueryWrapper<InvoiceDetailDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(InvoiceDetailDO::getInvoiceId, invoice.getId());
        List<InvoiceDetailDO> invoiceDetailDOList = invoiceDetailMapper.selectList(wrapper);
        LambdaQueryWrapper<InvoiceLogDO> logWrapper = new LambdaQueryWrapper<>();
        logWrapper.eq(InvoiceLogDO::getInvoiceId, invoice.getId());
        List<InvoiceLogDO> invoiceLogDOList = invoiceLogMapper.selectList(logWrapper);
        InvoiceInfoRespVO respVo = new InvoiceInfoRespVO();
        BaseInvoiceVO invoiceVO = BaseInvoiceConvert.INSTANCE.convert(invoice);
        invoiceVO.setInvoiceAmount("0");
        respVo.setInvoice(invoiceVO);
        respVo.setOrderDetailList(orderDetailList);
        respVo.setInvoiceDetailList(invoiceDetailDOList);
        respVo.setInvoiceLogList(invoiceLogDOList);
        return respVo;
    }


    @Override
    public PageResult<IssueInvoicePageRespVO> getIssueInvoiceList(IssueInvoicePageReqVO vo) {
        Page<IssueInvoicePageRespVO> page = new Page<>(vo.getPageNo(), vo.getPageSize());
        invoiceMapper.getIssueInvoiceList(page, vo);
        List<IssueInvoicePageRespVO> data = page.getRecords();
        if(CollectionUtils.isNotEmpty(data)){
            Set<Long> ids = data.stream().map(it->Long.valueOf(it.getApartmentId())).collect(Collectors.toSet());
            List<ApartmentListDto> apartments = apartmentApi.listByIds(new ArrayList<>(ids)).getCheckedData();
            data.forEach(it->{
                it.setApartmentName(apartments.stream().filter(ap->ap.getId().equals(Long.valueOf(it.getApartmentId()))).findFirst().get().getName());
            });
        }
        PageResult<IssueInvoicePageRespVO> result = new PageResult<>();
        result.setList(data);
        result.setTotal(page.getTotal());
        return result;
    }

    // 发票作废操作
    @Override
    public void invalidateInvoice(LoginUser user, Long invoiceId) {
        invoiceMapper.updateById(InvoiceDO.builder().id(invoiceId).status("5").build());
        contractRentOrderApi.updateOrderByInvalidateInvoice(invoiceId);
    }

    /**
     * 发票红冲
     *
     * @param user
     * @param invoiceId
     * @return
     */
    @Override
    public String invoiceRushRed(LoginUser user, Long invoiceId) {
        InvoiceDO invoice = invoiceMapper.selectById(invoiceId);
        if (null == invoice) {
            throw new ServiceException(FINANCE_INVOICE_RECORD_NOT_CONFIG);
        }
        if (!StringUtils.equals("2", invoice.getStatus())) {
            throw new ServiceException(FINANCE_INVOICE_RECORD_RUSH_RED);
        }
        CommonResult<String> result = contractRentOrderApi.checkOrderTime(invoiceId);
        if (StringUtils.equals("fail", result.getCheckedData())) {
            throw new ServiceException(FINANCE_INVOICE_RECORD_TIMEOUT);
        }
        String hpCOde = "HP" + timebaseSequenceGenerator.YYYYMMDDHHMMSS.next("lease:invoice:seq");
        invoice.setRedSeqNo(hpCOde);
        invoiceMapper.updateById(InvoiceDO.builder().id(invoiceId).redSeqNo(hpCOde).status("7").build());
        contractRentOrderApi.updateOrderByRushRed(invoiceId, 7);
//        FinanceInvoiceRedRecordDO.builder()
//                .blueRecordId(invoiceId)
//                .amount(invoice.getAmount())
//                .seqNo("FP" + timebaseSequenceGenerator.YYYYMMDDHHMMSS.next("lease:invoice:seq"))
//                .build();
//        FinanceInvoiceConfigDO config = financeInvoiceConfigService.getByApartmentId(invoice.getApartmentId());
        LambdaQueryWrapper<InvoiceDetailDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(InvoiceDetailDO::getInvoiceId, invoice.getId());
        List<InvoiceDetailDO> items = invoiceDetailMapper.selectList(wrapper);
        log.info("发票红冲项目明细：：：" + JSON.toJSONString(items));
        // 发票红冲方法
        FpChCreate fpChCreate = fpChConvert(hpCOde, invoice.getSeqNo(), "01", items);
        String results = BwServiceUtil.fpChCreate(fpChCreate);
        JSONObject jsonObject = JSONObject.parseObject(results);
        log.info("发票红冲请求，返回报文【" + hpCOde + "】：" + JSON.toJSONString(results));
        return jsonObject.getString("message");
    }

    /**
     * 百旺红冲接口
     *
     * @return
     */
    private void baiWangRushRed(InvoiceDO invoice, List<InvoiceDetailDO> items) {
        //费用明细组装
        List<FpFeeDetail> feeDetails = new ArrayList<>();
        for (InvoiceDetailDO detailDO : items) {
            FpFeeDetail fpFeeDetail = FpFeeDetail.builder()
                    .djhh(detailDO.getId().toString())
                    .tax(detailDO.getTaxRate())
                    .bhsdj(detailDO.getUnitAmount())
                    .bhsje(detailDO.getTotalAmount().subtract(detailDO.getTax()))
                    .hsje(detailDO.getTotalAmount())
                    .se(detailDO.getTax())
                    .build();
            feeDetails.add(fpFeeDetail);
        }
        //发票基础组装
        FpChCreate fpCreate = FpChCreate.builder()
                .djbh(invoice.getRedSeqNo())
                .ydjbh(invoice.getSeqNo())
                .hsje(invoice.getAmount())
                .chyydm("01")
                .mxxx(feeDetails)
                .build();
        String fp = BwServiceUtil.fpChCreate(fpCreate);
        System.out.println(fp);
        JSONObject jsonObject = JSONObject.parseObject(fp);
        if (StringUtils.equals(jsonObject.get("code").toString(), "-1")) {
            invoice.setStatus("9");
            invoice.setErrorReason(jsonObject.get("renson").toString());
            contractRentOrderApi.updateOrderByRushRed(invoice.getId(), 9);
        }
        invoiceMapper.updateById(invoice);

        BaiwangInvoiceLogDO.builder()
                .interfaceName(BwServiceEnum.FP_CH_CREATE.getSummary())
                .seqNo(invoice.getSeqNo())
                .dataParams(fpCreate.toString())
                .resultData(fp)
                .build();

        //冲红申请结果查询
        Map<String, String> map = new HashMap<>();
        map.put("djbh", invoice.getRedSeqNo());
        String result = BwServiceUtil.fpChStatusSearch(map);
        cn.hutool.json.JSONObject jsonObject1 = JSONUtil.parseObj(result);
        if (StringUtils.equals(jsonObject1.get("code").toString(), "-1")) {
            invoice.setStatus("9");
            invoice.setErrorReason(jsonObject1.get("renson").toString());
            contractRentOrderApi.updateOrderByRushRed(invoice.getId(), 9);
        } else {
            invoice.setStatus("7");
            contractRentOrderApi.updateOrderByRushRed(invoice.getId(), 7);
        }
        invoiceMapper.updateById(invoice);

        BaiwangInvoiceLogDO.builder()
                .interfaceName(BwServiceEnum.FP_CH_STATUS_SEARCH.getSummary())
                .seqNo(invoice.getSeqNo())
                .dataParams(invoice.getSeqNo())
                .resultData(result)
                .build();


    }

    @Override
    public OrderDetailInfoRespVO getDetailByOrderIds(List<Long> orderIdList) {
        List<OrderFeeDetailVO> orderDetailList = new ArrayList<>();
        StringBuilder remark = new StringBuilder();
        for (Long orderId : orderIdList) {
            List<Long> ids = Stream.of(orderId).collect(Collectors.toList());
            CommonResult<List<RentOrderExtVO>> result = contractRentOrderApi.getActualExpensesByOrderIds(ids);
            RentOrderExtVO vo = result.getCheckedData().stream().findFirst().get();
            remark.append(vo.getStartTime()).append("~").append(vo.getEndTime()).append("\n");
            List<OrderFeeDetailVO> detailVOList = vo.getFeeDetail();
            orderDetailList.addAll(detailVOList);
        }
        OrderDetailInfoRespVO vo = new OrderDetailInfoRespVO();
        vo.setRemark(remark.toString());
        vo.setOrderIdList(orderIdList);
        vo.setOrderDetailList(orderDetailList);
        return vo;
    }

    @Override
    public List<Long> invoiceRedForOrderIds(List<Long> orderIds) {
        List<Long> ids = new ArrayList<>();
        for (Long orderId : orderIds) {
            LambdaQueryWrapper<InvoiceDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(InvoiceDO::getStatus, 2)
                    .orderBy(true, false, InvoiceDO::getId)
                    .last("limit 1");

            InvoiceDO invoice = invoiceMapper.selectOne(lambdaQueryWrapper);
            if (null == invoice) {
                throw new ServiceException(FINANCE_INVOICE_RECORD_NOT_CONFIG);
            }
            if (!StringUtils.equals("2", invoice.getStatus())) {
                throw new ServiceException(FINANCE_INVOICE_RECORD_RUSH_RED);
            }

            String hpCOde = "HP" + timebaseSequenceGenerator.YYYYMMDDHHMMSS.next("lease:invoice:seq");
            invoice.setRedSeqNo(hpCOde);
            invoiceMapper.updateById(InvoiceDO.builder().id(invoice.getId()).redSeqNo(hpCOde).status("7").build());
            LambdaQueryWrapper<InvoiceDetailDO> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(InvoiceDetailDO::getInvoiceId, invoice.getId());
            List<InvoiceDetailDO> items = invoiceDetailMapper.selectList(wrapper);
            log.info("发票红冲项目明细：：：" + JSON.toJSONString(items));
            // 发票红冲方法
            FpChCreate fpChCreate = fpChConvert(hpCOde, invoice.getSeqNo(), "01", items);
            String results = BwServiceUtil.fpChCreate(fpChCreate);
            JSONObject jsonObject = JSONObject.parseObject(results);
            log.info("发票红冲请求，返回报文【" + hpCOde + "】：" + JSON.toJSONString(results));
            ids.add(orderId);
        }
        return ids;
    }


}
